package br.com.ps.bombertournament.game;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import br.com.ps.bombertournament.game.animation.Action;
import br.com.ps.bombertournament.game.animation.AnimationGameObject;
import br.com.ps.bombertournament.game.animation.Frame;
import br.com.ps.bombertournament.game.math.Rectangle;
import br.com.ps.bombertournament.game.util.TextureCache;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

import flexjson.JSONDeserializer;

/**
 * The {@link MappedGameObject} is responsible to create a game object with default properties founded in the configurations file.
 * 
 * @author Rennan Stefan Boni - rennanboni@gmail.com
 */
public abstract class MappedGameObject extends BaseGameObject {

	private static final String ALFA_COLOR = "alfacolor";
	private static final String ANIMATION_PROPERTY = "animation";
	private static final String NAMES_PROPERTY = "names";
	private static final String TEXTURE_PROPERTY = "texture";
	private static final String CLAZZ_PROPERTY = "clazz";
	private static final String ACTIONS_PROPERTY = "actions";
	private static final String DURATION_PROPERTY = "duration";
	private static final String FLIP_Y_PROPERTY = "flipY";
	private static final String FLIP_X_PROPERTY = "flipX";
	private static final String SCALE_Y_PROPERTY = "scale.y";
	private static final String SCALE_X_PROPERTY = "scale.x";
	private static final String Y_PROPERTY = "y";
	private static final String X_PROPERTY = "x";
	private static final String REGION_PROPERTY = "region";
	private static final String IMAGE_PROPERTY = "image";
	private static final String FRAMES_PROPERTY = "frames";
	private static final String LOOP_PROPERTY = "loop";
	private static final String NAME_PROPERTY = "name";
	private static final String ANIMATIONS_PROPERTY = "animations";
	private static final String WIDTH_PROPERTY = "width";
	private static final String HEIGHT_PROPERTY = "height";
	private static final String TOUCHABLE_PROPERTY = "touchable";
	private static final String VOLUME_PROPERTY = "volume";
	
	protected static final String CONFIGURATIONS_PROPERTY = "configurations";
	private WrapperMap configurations;
	

	/**
	 * Basic constructor
	 */
 	protected MappedGameObject() {
	}
 	
 	/**
 	 * Contains all properties configurations of the configurations file
 	 * 
 	 * @return Return a {@link WrapperMap} with all configurations properties
 	 */
	public WrapperMap getConfigurations() {
		return configurations;
	}

	/**
	 * Set new value of configurations properties.
	 * 
	 * @param configurations New value of configurations properties.
	 */
	public void setConfigurations(WrapperMap configurations) {
		this.configurations = configurations;
	}

	/**
	 * Load and apply basic configurations
	 */
	public void loadAnimations() {
		String json = Gdx.files.internal(getMapPath()).readString();

		WrapperMap configurations = new WrapperMap(new JSONDeserializer<Map<String, Object>>().deserialize(json));
		
		{ // Applying basic properties
			super.setTouchable(configurations.getBoolean(TOUCHABLE_PROPERTY, false));
			super.setBounds(
					super.getX(), super.getY(), 
					configurations.getFloat(WIDTH_PROPERTY, 0), configurations.getFloat(HEIGHT_PROPERTY, 0));
			
			super.setScale(configurations.getFloat(SCALE_X_PROPERTY, Renderer.scaleX), configurations.getFloat(SCALE_Y_PROPERTY, Renderer.scaleY));
			
			super.setTransparency(configurations.getFloat(ALFA_COLOR, 1));
			
			{ // Applying default texture
				if (configurations.containsKey(TEXTURE_PROPERTY)) {
					WrapperMap textureConfigurations = configurations.getMap(TEXTURE_PROPERTY);
					
					if (textureConfigurations.containsKey(IMAGE_PROPERTY)) {
						Texture texture = TextureCache.getInternal(textureConfigurations.getString(IMAGE_PROPERTY));
						
						TextureRegion textureRegion = null;
						if (textureConfigurations.containsKey(REGION_PROPERTY)) {
							WrapperMap regionCofiguration = textureConfigurations.getMap(REGION_PROPERTY);
							if (regionCofiguration.containsKey(X_PROPERTY) && regionCofiguration.containsKey(Y_PROPERTY)
									&& regionCofiguration.containsKey(WIDTH_PROPERTY) && regionCofiguration.containsKey(HEIGHT_PROPERTY)) {
								textureRegion = new TextureRegion(texture, 
										regionCofiguration.getInteger(X_PROPERTY), 
										regionCofiguration.getInteger(Y_PROPERTY),
										regionCofiguration.getInteger(WIDTH_PROPERTY), 
										regionCofiguration.getInteger(HEIGHT_PROPERTY));
								
								
								textureRegion.flip(textureConfigurations.getBoolean(FLIP_X_PROPERTY, false), textureConfigurations.getBoolean(FLIP_Y_PROPERTY, false));
							}
						} else {
							textureRegion = new TextureRegion(texture);
						}
						
						super.setCurrentTexture(textureRegion);
					}
				}
			}
		}
		
		{ // Loading configured animation
			Map<String, AnimationGameObject> animations = new HashMap<String, AnimationGameObject>();
			
			if (configurations.containsKey(ANIMATIONS_PROPERTY)) {
				List<WrapperMap> animationConfigurations = configurations.getList(ANIMATIONS_PROPERTY);
				
				for (WrapperMap animationConfiguration : animationConfigurations) {
					if (animationConfiguration.containsKey(NAME_PROPERTY)) {
						String name = animationConfiguration.getString(NAME_PROPERTY);
						animations.put(name, createAnimation(animationConfiguration));
					} else if (animationConfiguration.containsKey(NAMES_PROPERTY)) {
						List<WrapperMap> namesConfigurations = animationConfiguration.getList(NAMES_PROPERTY);
						
						if (namesConfigurations != null) {
							
							AnimationGameObject animation = createAnimation(animationConfiguration);
							for (WrapperMap nameConfigurations : namesConfigurations) {
								if (nameConfigurations.containsKey(NAME_PROPERTY)) {
									animations.put(nameConfigurations.getString(NAME_PROPERTY), animation);
								}
							}
						}
					}
				}
			}
			
			super.setAnimations(animations);
		}
		
		{ // Applying default animation
			if (configurations.containsKey(ANIMATION_PROPERTY)) {
				String animation = configurations.getString(ANIMATION_PROPERTY);
				
				super.play(animation);
			}
		}
		
		{ // Applying volume property
			if (configurations.containsKey(VOLUME_PROPERTY)) {
				WrapperMap volumeConfigurations = configurations.getMap(VOLUME_PROPERTY);
				if (volumeConfigurations.containsKey(WIDTH_PROPERTY) && volumeConfigurations.containsKey(HEIGHT_PROPERTY)) {
					super.setVolume(new Rectangle(super.getX(), super.getY(), volumeConfigurations.getInteger(WIDTH_PROPERTY), volumeConfigurations.getInteger(HEIGHT_PROPERTY)));
				}
			}
		}
	}
	
	/**
	 * Create a new animation with initial configuration
	 * @param animationConfiguration Animation properties to be load/interpreted
	 * @return Return a new animation to the game object
	 */
	private AnimationGameObject createAnimation(WrapperMap animationConfiguration) {
		AnimationGameObject result = new AnimationGameObject();
		
		result.setLoop(animationConfiguration.getBoolean(LOOP_PROPERTY, false));
		result.setName(animationConfiguration.getString(NAME_PROPERTY));
		
		List<WrapperMap> framesConfigurations = animationConfiguration.getList(FRAMES_PROPERTY);
		result.setFrames(createFrames(framesConfigurations));
		
		return result;
	}
	
	/**
	 * Create a new Frame with initial configuration
	 * 
	 * @param framesConfigurations List of frame properties to be load/interpreted
	 * @return Return a list of frame to animation of the game object
	 */
	private List<Frame> createFrames(List<WrapperMap> framesConfigurations) {
		List<Frame> result = null;
		if (framesConfigurations != null) {
			result = new ArrayList<Frame>();
			for (WrapperMap frameConfiguration : framesConfigurations) {
				result.add(createFrame(frameConfiguration));
			}
		}
		
		return result;
	}

	/**
	 * Create a new Frame with initial configuration
	 * 
	 * @param frameConfiguration Frame properties to be load/interpreted
	 * @return Return a frame to animation of the game object
	 */
	private Frame createFrame(WrapperMap frameConfiguration) {
		Frame result = null;
		
		if (frameConfiguration != null) {
			result = new Frame();
			
			{ // Adding texture
				if (frameConfiguration.containsKey(IMAGE_PROPERTY)) {
					Texture texture = TextureCache.getInternal(frameConfiguration.getString(IMAGE_PROPERTY));
					TextureRegion textureRegion = null;
					if (frameConfiguration.containsKey(REGION_PROPERTY)) {
						WrapperMap regionCofiguration = frameConfiguration.getMap(REGION_PROPERTY);
						if (regionCofiguration.containsKey(X_PROPERTY) && regionCofiguration.containsKey(Y_PROPERTY)
								&& regionCofiguration.containsKey(WIDTH_PROPERTY) && regionCofiguration.containsKey(HEIGHT_PROPERTY)) {
							textureRegion = new TextureRegion(texture, 
									regionCofiguration.getInteger(X_PROPERTY),
									regionCofiguration.getInteger(Y_PROPERTY),
									regionCofiguration.getInteger(WIDTH_PROPERTY), 
									regionCofiguration.getInteger(HEIGHT_PROPERTY));
							
							
							textureRegion.flip(frameConfiguration.getBoolean(FLIP_X_PROPERTY, false), frameConfiguration.getBoolean(FLIP_Y_PROPERTY, false));
						}
					} else {
						textureRegion = new TextureRegion(texture);
					}
					
					result.setTexture(textureRegion);
				}
			}
			
			result.setDuration(frameConfiguration.getFloat(DURATION_PROPERTY));
			if (frameConfiguration.containsKey(ACTIONS_PROPERTY)) {
				result.setActions(createActions(frameConfiguration.getList(ACTIONS_PROPERTY)));
			}
		}
		
		return result;
	}

	/**
	 * Create the actions
	 * 
	 * @param actions List of the properties to be load/interpreted
	 * @return Return the list of actions
	 */
	private List<Action> createActions(List<WrapperMap> actions) {
		List<Action> result = null;
		if (actions != null) {
			result = new ArrayList<Action>();
			for (WrapperMap action : actions) {
				result.add(createAction(action));
			}
		}
		
		return result;
	}

	/**
	 * Create a new action based on {@link WrapperMap} with properties 
	 * 
	 * @param action A {@link WrapperMap} with configurations
	 * @return Return new action
	 */
	private Action createAction(WrapperMap action) {
		Action result = null;
		
		if (action.containsKey(CLAZZ_PROPERTY)) {
			String clazzStr = action.getString(CLAZZ_PROPERTY);
			
			try {
				Class<?> clazz = Class.forName(clazzStr);
				
				result = (Action) clazz.newInstance();
				
				result.setParameters(action);
				
				result.create();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		
		return result;
	}
	
	/**
	 * Retorna o caminnho do arquivo de configura��o com as informa��es do objeto do jogo 
	 * 
	 * @return Retorna o caminho de configura��o do objeto do jogo.
	 */
	public abstract String getMapPath();
}
